#include "gen.i386.h"
#include "DiMPA_CPU.h"
#include "DiMPA_Client.h"

#include <tinyaml/vm.h>
#include "fastmath.h"
#include <tinyaml/thread.h>
#include <tinyaml/_impl.h>
#include <tinyaml/object.h>
#include <unistd.h>

pthread_t pt1;
pthread_attr_t pa1;

vm_blocker_t init_blocker;

extern char server_fifo_path[512];

extern int i386_main();

void* dmp_thread(void*d) {
	i386_main();
	return NULL;
}

void arch_infos();

void _VM_CALL vm_op_dmp_init(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
#if defined(CLIENT)&&!defined(MASTER)
	char proto[5] = { 0, 0, 0, 0, 0 };
	char target[4096];
	assert(d->type==DataString||d->type==DataObjStr);
	sscanf((const char*)d->data, "%4c:%s", proto, target);
	if(!strcmp(proto, "unix")) {
		strcpy(server_fifo_path, target);
	} else if(!strcmp(proto, "inet")) {
		vm_fatal("DMP Client : protocol inet not yet implemented (should come soon)\n");
	} else if(!strcmp(proto, "midi")) {
		vm_fatal("DMP Client : protocol midi not yet implemented (may not come soon)\n");
	} else {
		sprintf(target, "DMP Client : protocol %s not recognized\n", proto);
		vm_fatal(target);
	}
#endif
	init_blocker = blocker_new();

	assert(vm->current_thread);

	vm->current_thread->IP+=2;
	blocker_suspend(vm,init_blocker,vm->current_thread);

	pthread_attr_init(&pa1);
	pthread_attr_setschedpolicy(&pa1,SCHED_RR);
	pthread_attr_setdetachstate(&pa1,PTHREAD_CREATE_DETACHED);
	pthread_create(&pt1,&pa1,dmp_thread,NULL);
	sleep(1);
	arch_infos();
}


void _q_sync_cb(query_t q) {
	/*printf("* * * FREEING QUERY @%p blocker @%p\n", q, q->q.cb_data);*/
	if(q->q.status==QS_FAILED) {
		fputs("\n\n * * * QUERY FAILED ! * * *\n\n", stdout);
	}
	blocker_resume(_glob_vm,(vm_blocker_t)q->q.cb_data);
	/*blocker_free((vm_blocker_t)q->q.cb_data);*/
}

static void _q_prepare(int sync, void(**cb)(query_t), void**arg) {
	if(sync) {
		*cb = _q_sync_cb;
		*arg = (void*) blocker_new();
		_glob_vm->current_thread->IP+=2;
		blocker_suspend(_glob_vm,(vm_blocker_t)*arg,_glob_vm->current_thread);
	} else {
		*cb = NULL;
		*arg = NULL;
	}
}


void _VM_CALL vm_op_dmp_term(vm_t vm, word_t unused) {
	void (*cb) (query_t) = NULL;
	void* arg = NULL;
	_q_prepare(1, &cb, &arg);
	query_quit(-1, cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_cell_Int(vm_t vm, word_t sync) {
	vm_data_t context = _vm_pop(vm);
	vm_data_t size = _vm_pop(vm);
	vm_data_t type = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(context->type==DataInt);
	assert(size->type==DataInt);
	assert(type->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_cell(-1, (master_entity*) path->data, (const char*) name->data,
			type->data, size->data, context->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_proxy_Int(vm_t vm, word_t sync) {
	vm_data_t context = _vm_pop(vm);
	vm_data_t size = _vm_pop(vm);
	vm_data_t type = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(context->type==DataInt);
	assert(size->type==DataInt);
	assert(type->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_proxy(-1, (master_entity*) path->data, (const char*) name->data,
			type->data, size->data, context->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_patch_i_Int(vm_t vm, word_t sync) {
	vm_data_t context = _vm_pop(vm);
	vm_data_t size = _vm_pop(vm);
	vm_data_t type = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(context->type==DataInt);
	assert(size->type==DataInt);
	assert(type->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_patch_input(-1, (master_entity*) path->data, (const char*) name->data,
			type->data, size->data, context->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_patch_o_Int(vm_t vm, word_t sync) {
	vm_data_t context = _vm_pop(vm);
	vm_data_t size = _vm_pop(vm);
	vm_data_t type = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(context->type==DataInt);
	assert(size->type==DataInt);
	assert(type->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_patch_output(-1, (master_entity*) path->data, (const char*) name->data,
			type->data, size->data, context->data,
			cb, arg);
}



void _VM_CALL vm_op_dmp_q_new_patch_r_Int(vm_t vm, word_t sync) {
	vm_data_t context = _vm_pop(vm);
	vm_data_t size = _vm_pop(vm);
	vm_data_t type = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(context->type==DataInt);
	assert(size->type==DataInt);
	assert(type->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_patch_register(-1, (master_entity*) path->data, (const char*) name->data,
			type->data, size->data, context->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_patch_Int(vm_t vm, word_t sync) {
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_patch(-1, (master_entity*) path->data, (const char*) name->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_module_Int(vm_t vm, word_t sync) {
	vm_data_t spec = _vm_pop(vm);
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(spec->type==DataInt);
	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_module(-1, (master_entity*) path->data, (const char*) name->data,
			spec->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_new_group_Int(vm_t vm, word_t sync) {
	vm_data_t name = _vm_pop(vm);
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(name->type==DataString||name->type==DataObjStr);
	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_new_group(-1, (master_entity*) path->data, (const char*) name->data,
			cb, arg);
}


void _VM_CALL vm_op_dmp_q_del_ent_Int(vm_t vm, word_t sync) {
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_del_ent(-1, (master_entity*) path->data, cb, arg);
}


void _VM_CALL vm_op_dmp_q_get_cell_Int(vm_t vm, word_t sync) {
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_get_cell(-1, (master_entity*) path->data, cb, arg);
}




void _VM_CALL vm_op_dmp_q_cell_watch_Int(vm_t vm, word_t sync) {
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_watch(-1, (master_entity*) path->data, cb, arg);
}


void _VM_CALL vm_op_dmp_q_cell_unwatch_Int(vm_t vm, word_t sync) {
	vm_data_t path = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(path->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_unwatch(-1, (master_entity*) path->data, cb, arg);
}


void _VM_CALL vm_op_dmp_q_link_Int(vm_t vm, word_t sync) {
	vm_data_t cell = _vm_pop(vm);
	vm_data_t input = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(input->type==DataInt);
	assert(cell->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_link(-1, (master_entity*) input->data, (master_entity*) cell->data, cb, arg);
}


void _VM_CALL vm_op_dmp_q_unlink_Int(vm_t vm, word_t sync) {
	vm_data_t cell = _vm_pop(vm);
	vm_data_t input = _vm_pop(vm);

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(input->type==DataInt);
	assert(cell->type==DataInt);

	_q_prepare(sync, &cb, &arg);
	query_unlink(-1, (master_entity*) input->data, (master_entity*) cell->data, cb, arg);
}


void _VM_CALL vm_op_dmp_q_quit_Int(vm_t vm, word_t sync) {
	void (*cb) (query_t) = NULL;
	void* arg = NULL;
	_q_prepare(sync, &cb, &arg);
	query_srv_quit(-1, cb, arg);
}




void _VM_CALL vm_op_dmp_getent(vm_t vm, word_t unused) {
	vm_data_t path = _vm_pop(vm);
	vm_data_t cursor = _vm_pop(vm);
	word_t e;
	printf("getent %i:%s in %i:%X\n", path->type, (char*)path->data, cursor->type, cursor->data);
	assert(path->type==DataString||path->type==DataObjStr);
	assert(cursor->type==DataInt);
	e = (word_t)get_ent((master_entity*)cursor->data, (char*)path->data);
	/*if(!e) {*/
		/*vm_fatal("Entity not found.");*/
	/*}*/
	vm_push_data(vm, DataInt, e);
}


void _VM_CALL vm_op_dmp_ent_type(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	vm_push_data(vm, DataInt, ent_type(e));
}


void _VM_CALL vm_op_dmp_ent_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	vm_push_data(vm, DataString, (word_t)ent_name(e));
}


void _VM_CALL vm_op_dmp_ent_path(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	vm_push_data(vm, DataString, (word_t)ent_path(e));
}


void _VM_CALL vm_op_dmp_ent_next(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	vm_push_data(vm, DataInt, (word_t)ent_next(e));
}


void _VM_CALL vm_op_dmp_ent_context(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	vm_push_data(vm, DataInt, (word_t)ent_context(e));
}


void _VM_CALL vm_op_dmp_grp_children(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_grp_children(e));
}


void _VM_CALL vm_op_dmp_cell_links(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_cell_links(e));
}

void _VM_CALL vm_op_dmp_in_links(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_input_links(e));
}


void _VM_CALL vm_op_dmp_in_linked_cell(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_input_linked_cell(e));
}


void _VM_CALL vm_op_dmp_mod_inputs(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_mod_inputs(e));
}


void _VM_CALL vm_op_dmp_mod_outputs(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_mod_outputs(e));
}


void _VM_CALL vm_op_dmp_mod_registers(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*e = (master_entity*) d->data;
	assert(d->type==DataInt);
	assert(ent_type(e)==ENT_GROUP||ent_type(e)==ENT_PATCH);
	vm_push_data(vm, DataInt, (word_t)ent_mod_registers(e));
}




void _VM_CALL vm_op_dmp_cell_set_data_Int(vm_t vm, word_t sync) {
	vm_data_t dd = _vm_pop(vm);
	dynarray_t d = (dynarray_t)dd->data;
	vm_data_t dc = _vm_pop(vm);
	master_entity*c = (master_entity*)dc->data;
	int ht, htsz;
	word_t sz;

	void (*cb) (query_t) = NULL;
	void* arg = NULL;

	assert(dc->type==DataInt);

	dmp_celltype_to_hardtype(ent_cell_type(c), &ht, &htsz);
	sz = ent_cell_size(c)*htsz;
	_q_prepare(sync, &cb, &arg);
	if(sz==1) {
		#if 0
		switch(ht) {
		case 1:/*float*/
			assert(dd->type==DataFloat);
			break;
		case 0:/*int*/
		default:;
			assert(dd->type==DataInt);
			break;
		};
		#endif
		query_set_cell(-1, c, (word_t)dd->data, cb, arg);
	} else {
		word* ptr = (word*) alloc_buf(sz);
		int i;
		query_t q;
		for(i=0;i<sz;i++) {
			ptr[i] = dynarray_get(d, (i<<1)+1);
		}
		q = _query_set_cell(-1, c, (word_t)ptr, cb, arg);
		q->q.query_type = Q_SET_CELL_BUF_MANAGED;
		query_submit(q);
	}
}


void _VM_CALL vm_op_dmp_cell_get_data(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	master_entity*c = (master_entity*) d->data;
	int ht, htsz;
	word_t sz;
	assert(d->type==DataInt);
	dmp_celltype_to_hardtype(ent_cell_type(c), &ht, &htsz);
	sz = ent_cell_size(c)*htsz;
	if(sz==1) {
		#if 0
		switch(ht) {
		case 1:/*float*/
			printf("get Float cell %lX\n", c->cell.data);
			vm_push_data(vm, DataFloat, c->cell.data);
			break;
		case 0:/*int*/
		default:;
			printf("get Int cell %lX\n", c->cell.data);
			vm_push_data(vm, DataInt, c->cell.data);
			break;
		};
		#endif
	} else {
		int i;
		int dt = ht==1?DataFloat:DataInt;
		dynarray_t obj = vm_array_new();
		dynarray_reserve(obj, sz<<1);
		for(i=0;i<sz;i++) {
			dynarray_set(obj, i<<1, dt);
			dynarray_set(obj, (i<<1)+1, ((word_t*)c->cell.data)[i]);
		}
		vm_push_data(vm, DataObjArray, (word_t)obj);
	}
}




void _VM_CALL vm_op_dmp_type_by_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	const char*name = (const char*)d->data;
	int t = 0;
	/*printf("DEBUG : type=%i\n", d->type);*/
	assert(d->type==DataString||d->type==DataObjStr);

	while(t<dmp_count_celltypes()&&strcmp(name, dmp_celltype_name(t))) {
		t++;
	}
	if(t==dmp_count_celltypes()) {
		char buf[256] = "TypeNotFound:";
		strcat(buf, name);
		vm_fatal(buf);
	} else {
		vm_push_data(vm, DataInt, t);
	}
}

void _VM_CALL vm_op_dmp_type_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	int t = 0;
	assert(d->type==DataInt&&d->data<dmp_count_celltypes());

	vm_push_data(vm, DataString, (word_t)dmp_celltype_name(d->data));
}

void _VM_CALL vm_op_dmp_type_count(vm_t vm, word_t unused) {
	vm_push_data(vm, DataInt, dmp_count_celltypes());
}

void _VM_CALL vm_op_dmp_spec_by_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	const char*name = (const char*)d->data;
	int t = 0;
	assert(d->type==DataString||d->type==DataObjStr);

	while(t<dmp_count_modspecs()&&strcmp(name, dmp_modspec_name(t))) {
		t++;
	}
	if(t==dmp_count_modspecs()) {
		vm_fatal("Module spec not found.");
	} else {
		vm_push_data(vm, DataInt, t);
	}
}

void _VM_CALL vm_op_dmp_spec_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	int t = 0;
	assert(d->type==DataInt&&d->data<dmp_count_modspecs());

	vm_push_data(vm, DataString, (word_t)dmp_modspec_name(d->data));
}

void _VM_CALL vm_op_dmp_spec_count(vm_t vm, word_t unused) {
	vm_push_data(vm, DataInt, dmp_count_modspecs());
}

void _VM_CALL vm_op_dmp_subsys_by_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	const char*name = (const char*)d->data;
	int t = 0;
	assert(d->type==DataString||d->type==DataObjStr);

	while(t<dmp_subsys_count()&&strcmp(name, dmp_subsys_name(t))) {
		t++;
	}
	if(t==dmp_subsys_count()) {
		vm_fatal("Subsystem not found.");
	} else {
		vm_push_data(vm, DataInt, t);
	}
}

void _VM_CALL vm_op_dmp_subsys_name(vm_t vm, word_t unused) {
	vm_data_t d = _vm_pop(vm);
	int t = 0;
	assert(d->type==DataInt&&d->data<dmp_subsys_count());

	vm_push_data(vm, DataString, (word_t)dmp_subsys_name(d->data));
}


void _VM_CALL vm_op_dmp_subsys_count(vm_t vm, word_t unused) {
	vm_push_data(vm, DataInt, dmp_subsys_count());
}


